#!/usr/bin/env perl
# The above shebang is for "perlbrew", otherwise use /usr/bin/perl
#
# Please refer to the Plain Old Documentation (POD) at the end of this Perl Script for further information


use strict;
use Carp;
use Pod::Usage;
use JSON;
use LWP::UserAgent;
use Getopt::Long;
use Data::Dumper;
# CONFIGURATION Remove "#" for Smart::Comments
# use Smart::Comments;


my $VERSION = "0.0.1"; # May be required to upload script to CPAN i.e. http://www.cpan.org/scripts/submitting.html


# Supported Output Plug-Ins
my $tit_output_plugins = "FQDN_CSV, TCP_FQDN_CSV, MALTEGO_CSV, NMAP_SH, NC_SH";


# Bing Search API - Required Parameters
#
# TODO Move the scalars below to a configuration file as per the recommendation(s) specified within Perl Best Practices
# AppId i.e. http://www.bing.com/toolbox/bingdeveloper/
my $bing_request_apiid = "9F3E29142254AAE98E9D8E466CCA278851CC092F";
# #CONFIGURATION Version, default value is "2.1" i.e. http://msdn.microsoft.com/en-us/library/dd250880.aspx
my $bing_request_version = "2.1";
# Sources i.e. http://msdn.microsoft.com/en-us/library/dd250895.aspx
my $bing_request_source = "Web";


# Bing Search API - Optional Parameters
# TODO Integrate into $bing_json_url
# #CONFIGURATION Market i.e. http://msdn.microsoft.com/en-us/library/dd251064.aspx
my $bing_request_market = "en-AU";
# TODO Integrate into $bing_json_url
# #CONFIGURATION Adult Enumeration i.e. http://msdn.microsoft.com/en-us/library/dd251007.aspx
my $bing_request_adult = "Off";
# Count i.e. http://msdn.microsoft.com/en-us/library/dd251004.aspx
my $bing_request_count = "50";


# Command line arguments for the Bing Search API
# Query i.e. http://msdn.microsoft.com/en-us/library/dd250887.aspx
my $bing_request_query;
# site: Bing Search Opeartor
my $bing_request_query_site;
# Offset i.e. http://msdn.microsoft.com/en-us/library/dd250920.aspx
my $bing_request_offset = "0";

# Command line meta-options
my $usage;
my $man;
# TODO "update" disabled, refer to #Team_Explorer below
# my $update;
# TODO #tit_kludge $quiet should be deprecated once the subroutine for the tit-kludge.pl workaround has been released
my $quiet;

# TODO Display -usage if command line argument(s) are incorrect
GetOptions(
    "site=s"  => \$bing_request_query_site, 
    "query=s" => \$bing_request_query,
    "offset=i" => \$bing_request_offset,

    # Command line meta-options 
    # -version is excluded as it is printed prior to processing the command line arguments
    # TODO -verbose
    "usage"   => \$usage,
    "man"     => \$man,
    # TODO #tit_kludge $quiet should be deprecated once the subroutine for the tit-kludge.pl workaround has been released
    "quiet"   => \$quiet
    # TODO "update" disabled, refer to #Team_Explorer below
    # "update"  => \$update
);

if (($usage eq 1) or ($man eq 1)) {
    pod2usage(-verbose => 2);
    die();
}

# TODO #TEAM_EXPLORER - Port from Subversion to Team Explorer
# if ($update eq 1) {
#    print "Please execute \"svn update\" from the command line\n";
#    die();
#}

if ($quiet == 0) {
	print "\n\"TCP Input Text\" for Bing Alpha (Release Candidate) RC v$VERSION\n";
	print "\n";
	print "Copyright 2008-2012 Christian Heinrich\n";
	print "Licensed under the Apache License, Version 2.0\n\n";
	print "Output Plug-Ins: $tit_output_plugins\n\n";
}

# Process the command line arguments
chomp($bing_request_query);

# Subsitute -site for -query command line arguement
if ($bing_request_query eq "") {
	# TODO Check $bing_request_query_site has a value if $bing_request_query doesn't
	$bing_request_query = "site:" . "$bing_request_query_site";
}

# Strip ":" from Bing search operator for filename
# TODO Expand this to strip illegal filename chars i.e. \/:*?<>|
my $stripped_query = $bing_request_query;
$stripped_query =~ s/://g;
my $dir = "$stripped_query/tit/bing";

# The directory of the comma separated .csv files and shell scripts .sh of nmap and nc aka netcat
if ( !( -e $dir ) ) {
    if ($quiet == 0) {
    	print ("Creating ./$dir\n\n");
    }
    if ( !( -e "./$stripped_query") ) {
        mkdir ("./$stripped_query");
        # TODO Confirm existance of "./$stripped_query/tit"
        mkdir ("./$stripped_query/tit");
        # TODO Confirm existance of "./$stripped_query/tit/bing"
        mkdir ("./$stripped_query/tit/bing");
		# TODO Create a separate directory for json_debug_log.txt
    }
}
else { if ($quiet == 0) {
	print "Appending ./$dir\n\n"; 
	}
	print "oOo"
}

# Create a new Bing search JSON request
# TODO Refactor as a subroutine i.e. sub Bing_Search
# TODO Refactor with the Bing::Search CPAN Module
# TODO Support XML of Bing API v2.0 i.e. http://api.bing.com/xml.aspx i.e. http://msdn.microsoft.com/en-us/library/dd250893.aspx as an alternative to JSON and SOAP
# TODO Support SOAP of Bing API v2.0 as an alternative to JSON and XML
my $bing_json_url = "http://api.bing.net/json.aspx?Appid=$bing_request_apiid&query=$bing_request_query&sources=$bing_request_source&Web.Count=$bing_request_count&Web.Offset=$bing_request_offset";
# "###" is for Smart::Comments CPAN Module
### \$bing_json_url is: $bing_json_url
# TODO Replace LWP::UserAgent with HTTP::Tiny CPAN Module
# TODO Replace LWP::UserAgent with libwhisker
my $bing_http_request = LWP::UserAgent->new;
# TODO Availability of $bing_json_url i.e. is "up" and resulting HTTP Status Code
my $bing_http_response = $bing_http_request->get("$bing_json_url")->content;
# decode_json returns a reference to a hash
# TODO -debug flag as a command line argument
open( DEBUG_LOG, ">>./$dir/json_debug_log.txt" );
print DEBUG_LOG "# \$bing_json_url\n\n";
print DEBUG_LOG "$bing_json_url\n\n";
print DEBUG_LOG "# \$bing_http_request->get(\$bing_json_url)->content\n\n";
print DEBUG_LOG ( Data::Dumper::Dumper(decode_json($bing_http_response) ) );
my $bing_json_searchresponse_web_results_ref = decode_json($bing_http_response)->{SearchResponse}->{Web}->{Results};
my @bing_response_json_web_results = @$bing_json_searchresponse_web_results_ref;
print DEBUG_LOG "# decode_json(\$bing_http_response)->{SearchResponse}->{Web}->{Results}\n\n";
print DEBUG_LOG ( Data::Dumper::Dumper(@bing_response_json_web_results) );
close DEBUG_LOG;
# TODO Highlight in red the creation of json_debug_log.txt in Term::ANSIColor
# "###" is for Smart::Comments CPAN Module
### json_debug_log.txt is generated within: $dir


open( FQDN_CSV, ">>./$dir/tit_fqdn.csv" );
open( TCP_FQDN_CSV, ">>./$dir/tit_tcp_fqdn.csv" );
open( MALTEGO_CSV, ">>./$dir/tit_maltego.csv" );
open( NMAP_SH,      ">>./$dir/tit_nmap.sh" );
open( NC_SH, ">>./$dir/tit_nc.sh" );

# Loop through the results.
# TODO Refactor parsing of Bing search results as a subroutine
my $bing_search_number_of_results = scalar (@bing_response_json_web_results);
for (my $bing_search_result_number = 0; $bing_search_result_number < $bing_search_number_of_results; $bing_search_result_number++) {
	my $URL = $bing_response_json_web_results[$bing_search_result_number]{'Url'} . "\n";	

    # Extract Protocol, Domain_Name, TCP_Port, $URI
	$URL =~ m|(\w+)://([^/:]+)(:\d+)?/(.*)|;
    my $protocol    = $1;
    my $domain_name = $2;
    my $URI         = ( "/" . $4 );
    my $TCP_port;

    # TODO $TCP_Port (i.e. $3) being uninitialized i.e. perl -w
    if   ( $3 =~ /:(\d+)/ ) { $TCP_port = $1 }
    if ($1 eq "https") { $TCP_port = "443" }
	if ($1 eq "http") { $TCP_port = "80" }
	my $parsed_search_result = (($bing_request_offset + $bing_search_result_number + "1") . ". $protocol(TCP/$TCP_port) open at $domain_name");
    # "###" is for "Smart::Comments CPAN Module
    ### $parsed_search_result
    
    # TODO Refactor as a subroutine for output plug-ins()
    print FQDN_CSV     ("$domain_name\n");
    print TCP_FQDN_CSV ("$domain_name,$TCP_port\n");
	print MALTEGO_CSV  ("$domain_name,$protocol,$TCP_port\n");
    print NMAP_SH      ("nmap -PN -sS -p T:$TCP_port $domain_name\n");
    print NC_SH        ("nc -vz $domain_name $TCP_port\n");
    # TODO output plug-in for curl and/or wget
    # TODO output plug-in for translate.google.com "Speak English or Die" workaround
}

# TODO Catch JSON returned error(s)
# Reduce the value of $bing_request_count for the final set of Bing search results
my $bing_json_searchresponse_web_ref = decode_json($bing_http_response)->{SearchResponse}->{Web};
my %bing_json_searchresponse_web = %$bing_json_searchresponse_web_ref;
my $bing_response_total = $bing_json_searchresponse_web{'Total'};
if ($bing_response_total < ($bing_request_count + $bing_request_offset) ) {
 	$bing_request_count = $bing_response_total - $bing_request_offset;
	# "###" is for "Smart::Comments CPAN Module
	### \$bing_response_total is: $bing_response_total
 	### \$bing_request_count is: $bing_request_count
}

# TODO Restrict number of concurrent queries per second to seven (7) as quoted within http://www.bing.com/developers/s/APIBasics.html
# TODO Automatically increment $bing_request_offset+$bing_request_count < 1000 based on $bing_response_total
# TODO Remove duplicate Bing search results


close FQDN_CSV;
close TCP_FQDN_CSV;
close MALTEGO_CSV;
close NMAP_SH;
close NC_SH;


=head1 NAME

tit.pl - "TCP Input Text"

=head1 VERSION

This documentation refers to tit Alpha v0.0.1

=head1 CONFIGURATION

Set the value(s) marked as #CONFIGURATION above this POD
	
=head1 USAGE

tit.pl -query [Bing Search Query]

=head1 REQUIRED ARGUEMENTS

 -query     Bing Search Query
 -site      Alternate Bing Query i.e. inserts site: Bing Search Operator
 				
=head1 OPTIONAL ARGUEMENTS

 -man		Displays POD and exits.
 -usage		Displays POD and exits.
 -update	Displays the Team Explorer command to retrieve the latest update from codeplex.com

=head1 DESCRIPTION

"TCP Input Text" implements the Bing API Version 2 to extract TCP Ports and Fully Qualified Domain Names (FQDN) from Bing Search Results into a .csv file and individual shell scripts for nmap and nc aka netcat to provide assurance of a listening TCP service. since the time that has past of the last crawl performed by the "MSNBot"/"BingBot".

=head1 DEPENDENCIES

=head1 PREREQUISITES

JSON CPAN Module
LWP::UserAgent CPAN Module

=head1 COREQUISITES

=head1 OSNAMES

osx

=head1 SCRIPT CATEGORIES

Web

=head1 INCOMPATIBILITIES

=head1 BUGS AND LIMITATIONS

Please refer to the comments beginning with "TODO" in the Perl Code.

=head1 AUTHOR

Christian Heinrich

=head1 CONTACT INFORMATION

http://cmlh.id.au/contact

=head1 MAILING LIST

=head1 REPOSITORY

http://tit.codeplex.com/

=head1 FURTHER INFORMATION AND UPDATES

http://tcpinputtext.org
http://del.icio.us/cmlh/tit

=head1 LICENSE AND COPYRIGHT

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. 

Copyright 2008-2012 Christian Heinrich
